﻿#define  _CRT_SECURE_NO_WARNINGS



// 继承:访问限定符,作用域,友元,静态成员，基类和派生类对象复制转换，派生类四个默认成员函数，多继承，不能被继承的类




#include <iostream>
#include <string>
using namespace std;



// 
// 
// 面向对象的三大特性：
// 封装
// 继承
// 多态
// 
// 
// Java支持反射
// 
// 
// 
// 继承是类层次的复用
// 
// 
// 如果多个类同时具有很多公共的特征，那个就把这些类中公共的部分提取出来，组成一个新的类
// 这个新的类就是这个几个类的父类（基类）
// 
// 
// 
// 
// 父类/基类
//class Person
//{
//public:
//	void Print()
//	{
//		cout << "name:" << _name << endl;
//		cout << "age:" << _age << endl;
//	}
////protected:// 父类设为保护，子类可见（不能用） 只能防外人
//private:// 父类为私有，子类不可见（子类也不能用）不仅仅可以防外人还能防儿子
//	string _name = "peter"; // 姓名
//	int _age = 18;//  年龄
//};
////
//// 继承该父类的子类 Student
//class Student : public Person
//	// 继承方式总共有三种：
//	// public公有继承
//	// private私有继承
//	// protect保护继承
//{
//	void func()
//	{
//		//cout << _name << endl;// 父类为私有，子类不可见
//	}
//
//protected:
//	int _stuid; // 学号
//};
//// 
//// 继承父类的子类 Teacher
////class Teacher : public Person
//class Teacher : Person// 继承的访问限定符也可以不写，默认class为private，class为public
//{
//protected:
//	int _jobid; // 工号
//};


// 父类/基类，子类/派生类


//int main()
//{
//	Student s;// 实例化一个学生类对象
//	s.Print();
//
//	return 0;
//}
// 
// 
//类成员/继承方式				public继承				protected继承					private继承
//  
//基类的public成员			派生类的public成员		派生类的protected成员			派生类的private成员
//基类的protected成员		派生类的protected成员	派生类的protected成员			派生类的private成员
//基类的private成员			在派生类中不可见		在派生类中不可见				在派生类中不可见
// 
// 
// 一个东西不想被子类继承就可以定义私有
// 
// 
// 
// 
// 
// 
// 
// 
// 
// 总结：
// 1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私
// 有成员还是被继承到了派生类对象中，但是语法上限制派生类对象不管在类里面还是类外面
// 都不能去访问它。
//
// 2. 基类private成员在派生类中是不能被访问，如果基类成员不想在类外直接被访问，但需要在
// 派生类中能访问，就定义为protected。可以看出保护成员限定符是因继承才出现的。
//
// 3. 实际上面的表格我们进行一下总结会发现，基类的私有成员在子类都是不可见。基类的其他
// 成员在子类的访问方式 == Min(成员在基类的访问限定符，继承方式)，public > protected
// > private。
//
// 4. 使用关键字class时默认的继承方式是private，使用struct时默认的继承方式是public，不过
// 最好显示的写出继承方式。
//
// 5. 在实际运用中一般使用都是public继承，几乎很少使用protetced / private继承，也不提倡
// 使用protetced / private继承，因为protetced / private继承下来的成员都只能在派生类的类里
// 面使用，实际中扩展维护性不强。
// 
// 
// 大多数时候：
// 基类：public/protect成员
// 派生类型：public继承
// 
// 










// 基类和派生类对象复制转换
// 
// 
// 
// 
//int main()
//{
//	double d = 1.1;
//	int i = d;// 隐式类型转换，会产生临时变量
//	const int& ri = d;	// 报错，d为double类型，隐式转换过程中产生一个int类型的临时变量，而这个临时变量具有常性，
//						// 用int&就扩大了操作域，需要const int&
//
//	// 把一个子类对象赋值给父类对象
//	Student s;
//	// 派生类对象赋值给基类对象
//	Person p = s;// 天然支持，不存在类型转换的发生，没有临时对象的产生
//	// 派生类对象给基类引用对象
//	Person& rp = s;// 不产生临时变量
//	// 派生类对象地址给基类指针对象
//	Person* ptrp = &s;// 指针也可以单独指向切割出来的父类内容
//
//	// 注意：
//	// 基类对象不可以给派生类对象，但指针除外
//
//	return 0;
//}
// 
// 














// 继承中的作用域
// 
// 
// 
// 
// 
//// Student的_num和Person的_num构成隐藏关系，可以看出这样代码虽然能跑，但是非常容易混淆
//class Person
//{
//protected:
//	string _name = "小李子"; // 姓名
//	int _num = 111;// 身份证号
//};
//
//class Student : public Person
//{
//public:
//	void Print()
//	{
//		cout << _num << endl;// 这个访问的是派生类的对象
//		cout << Person::_num << endl;// 需要指定作用域才能访问到父类成员
//
//		// 子类和父类有同名成员，子类成员将屏蔽掉父类的成员，直接访问子类成员，这种现象叫（隐藏 / 重定义）
//	}
//protected:
//	int _num = 999; // 学号
//};
//
//int main()
//{
//	Student s1;
//	s1.Print();
//
//	return 0;
//};
// 
// 
// 重点：
// 
// 1.基类和派生类都有独立的作用域
// 2.子类和父类有同名成员，子类成员将屏蔽掉父类的成员，直接访问子类成员，这种现象叫（隐藏/重定义）
// 3.子类和父类，只要成员函数名相同就构成隐藏
// 4.最好不要在继承体系里，有同名成员
// 
// 
// 
// 选择题：
// 
//class A
//{
//public:
//	void fun()
//	{
//		cout << "func()" << endl;
//	}
//};
//class B : public A
//{
//public:
//	void fun(int i)
//	{
//		A::fun();
//		cout << "func(int i)->" << i << endl;
//	}
//};
//void Test()
//{
//	B b;
//	b.fun(10);
//	b.A::fun();
//
//}; 

// A:两个fun构成函数重载
// B:两个fun构成隐藏
// C:编译错误
// D:以上说法都不对
// 
//	 
// B中的fun和A中的fun不是构成重载，因为不是在同一作用域
// B中的fun和A中的fun构成隐藏，成员函数满足函数名相同就构成隐藏。

//int main()
//{
//	Test();
//
//	return 0;
//}

















// 派生类的默认成员函数
// 
// 
// 
// 
// 
// 
//class Person
//{
//public:
//	// 默认构造
//	Person(const char* name = "peter")
//		: _name(name)
//	{
//		cout << "Person()" << endl;
//	}
//	// 拷贝构造
//	Person(const Person& p)
//		: _name(p._name)
//	{
//		cout << "Person(const Person& p)" << endl;
//	}
//	// 赋值重载
//	Person& operator=(const Person& p)
//	{
//		cout << "Person operator=(const Person& p)" << endl;
//		if (this != &p)
//			_name = p._name;
//
//		return *this;
//	}
//	// 析构函数
//	~Person()
//	{
//		cout << "~Person()" << endl;
//	}
//protected:
//	string _name; // 姓名
//};



//class Student : public Person
//{
//	// 派生类的构造函数
//	// 父类的成员必须调用父类的构造函数初始化
//public:
//	Student(const char* name, int num)
//		:Person(name)// 调用父类构造函数进行初始化
//		,_num(num)
//		//:_num(num)// 即使不显示调用父类的构造函数，也会自动调用父类的默认构造函数，若父类没有默认构造则必须显示调用
//	{
//		cout << "Student()" << endl;
//	}
//
//	// 派生类的拷贝构造函数
//	Student(const Student& s)
//		:Person(s)// 调用父类的拷贝构造函数，传入子类对象，让父类的引用对象接收即可（切片）
//		,_num(s._num)
//	{
//		cout << "Student(const Student& s)" << endl;
//	}
//
//	// 派生类的赋值重载函数
//	Student& operator=(const Student& s)
//	{
//		if (this != &s)
//		{
//			Person::operator=(s);// 调用父类的赋值重载函数，父类子类的operator=构成隐藏，注意指明作用域
//			_num = s._num;
//		}
//
//		cout << "Student& operator=(const Student& s)" << endl;
//
//		return *this;
//	}
//
//	// 派生类的析构函数
//	~Student()
//	{
//		// 由于以后要构成多态的原因，所以一个类的析构函数要处理成destructor（析构）
//		// 子类和父类的析构函数实际上的名称都是destructor两个构成了隐藏，所以需要指明作用域
//		//Person::~Person();
//		// 
//		// 构造时的顺序，先父后子
//		// 
//		// 为了保证析构顺序，先子后父，所以不允许显示的调用父类的析构函数
//		// 子类的析构函数结束时，会自动的调用父类的析构函数，保证析构顺序先子后父
//		cout << "~Student()" << endl;
//	}
//protected:
//	int _num;// 学号
//};


//int main()
//{
//	Student s1("qyw", 20);// 子类对象实例化时，会自动调用父类构造，也会自动调动父类的析构
//	Student s2(s1);
//
//	Person p = s1;// 当一个子类对象赋值给父类对象，自动调用父类对象的拷贝构造函数
//
//	s1 = s2;
//
//	return 0;
//}
	














// 继承与友元
// 
// 
// 
// 
// 友元关系不能继承，也就是说基类友元不能访问子类私有和保护成员
// 
//class Student;// 类声明
//
//class Person
//{
//public:
//	friend void Display(const Person& p, const Student& s);
//protected:
//	string _name; // 姓名
//};
//
//class Student : public Person
//{
//public:
//	friend void Display(const Person& p, const Student& s);// 给子类增加友元函数
//protected:
//	int _stuNum;// 学号
//};
//
//void Display(const Person& p, const Student& s)
//{
//	cout << p._name << endl;
//	cout << s._stuNum << endl;// 友元关系不能被继承，两个是独立的，所以不能访问子类的protected成员变量
//}
//
//void main()
//{
//	Person p;
//	Student s;
//	Display(p, s);
//}
// 
// 











// 继承与静态成员
// 
// 
// 
// 
// 基类定义了static静态成员，则整个继承体系里面只有一个这样的成员。
// 无论派生出多少个子类，都只有一个static成员实例。
// 
// 
//class Person
//{
//public:
//	Person() { ++_count; }// 子类对象实例化，必须调用父类的构造函数，所以创建一个对象就意味着调用了一次父类构造函数
////protected:
//	string _name; // 姓名
//
//	// 静态成员变量
//public:
//	static int _count; // 统计人的个数。
//};
//// 静态成员变量初始化
//int Person::_count = 0;
//
//
//class Student : public Person
//{
//protected:
//	int _stuNum; // 学号
//};
//
//class Graduate : public Student
//{
//protected:
//	string _seminarCourse; // 研究科目
//};
//
//int main()
//{
//	Student s;
//	Person p;
//	// 父类对象的_name 和 子类对象的_name 不是同一个对象初始化产生的
//	cout << &(p._name) << endl;
//	cout << &(s._name) << endl;
//
//	// 但父类的static 静态变量，在整个父类和子类中，都是仅有的唯一一个
//	cout << &(p._count) << endl;
//	cout << &(p._count) << endl;
//
//	// 统计Person基类总共继承了多少个子类对象
//	cout << Person::_count << endl;
//	cout << Student::_count << endl;
//	cout << Graduate::_count << endl;
//
//	return 0;
//}









// 实现一个不能被继承的类
// 
// 
// 
// 
// 方法：
// 把这个类的构造函数 或 析构函数private私有化，这个类就不能被继承了
// 
//class A
//{
//	// 由于A的构造函数设为私有，需要多提供一个 public的static静态成员函数 调用A的构造函数
//public:
//	static A CreateObj()
//	{
//		return A();// 直接创建一个匿名对象返回
//	}
//
//
//private:
//	A()
//	{}
////private:
////	~A()
////	{}
//};
//
//class B : public A
//{
//	 
//};
//
//int main()
//{
//	// 拷贝构造，创建一个a的A类对象
//	A a = A::CreateObj();
//	//B bb;
//
//	return 0;
//}














// 复杂的菱形继承及菱形虚拟继承   ----   多继承
// 
// 
// 
// 
// 
// 单继承：
//			一个子类只有一个直接父类时称这个继承关系为单继承
// 
// 多继承：
//			一个子类有两个或以上直接父类时称这个继承关系为多继承
// 
// 菱形继承：
//			菱形继承是多继承的一种特殊情况。
// 
// 
// 
// 
// 菱形继承问题：
// 数据冗余 和 二异性
// 
class Person
{
public:
	string _name; // 姓名
};


class Student : virtual public Person// 增加virtual虚继承
{
protected:
	int _num; //学号
};
class Teacher : virtual public Person// 增加virtual虚继承
{
protected:
	int _id; // 职工编号
};


class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程
};


int main()
{
	// 这样会有二义性无法明确知道访问的是哪一个
	Assistant a;
	// Assistant先调用构造函数，再Student 和 Teacher调用构造函数
	// 此时Student 和 Teacher 分别调用 Person的构造函数，Person的构造函数被调用了两次
	// Student 和 Teacher 各自包含了 Person内容
	// 当Assistant对象继承时，则会有分别为Student 和 Teacher两个的Person内容，
	// 导致 数据冗余 从而产生 二异性
	 
	
	a._name = "peter";

	// 需要显示指定访问哪个父类的成员可以解决二义性问题，但是数据冗余问题无法解决
	a.Student::_name = "强同学";
	a.Teacher::_name = "强老师";

	// 最后，为了解决多继承的 数据冗余 和 二异性   -------   增加 virtual虚继承
	// 
	// 无论那一个_name都是指代的唯一的一个

	return 0;
}
// 
// 
// 
// 
//